package com.powernode.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powernode.common.constant.RedisConstant;
import com.powernode.common.execption.PowerException;
import com.powernode.common.result.ResultCodeEnum;
import com.powernode.model.entity.order.*;
import com.powernode.model.enums.OrderStatus;
import com.powernode.model.form.order.OrderInfoForm;
import com.powernode.model.form.order.StartDriveForm;
import com.powernode.model.form.order.UpdateOrderBillForm;
import com.powernode.model.form.order.UpdateOrderCartForm;
import com.powernode.model.vo.order.CurrentOrderInfoVo;
import com.powernode.order.mapper.OrderBillMapper;
import com.powernode.order.mapper.OrderInfoMapper;
import com.powernode.order.mapper.OrderProfitsharingMapper;
import com.powernode.order.mapper.OrderStatusLogMapper;
import com.powernode.order.service.OrderInfoService;
import com.powernode.order.service.OrderMonitorService;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 订单信息服务实现类
 * 提供订单信息的CRUD操作和业务逻辑处理
 */
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {
    @Resource
    private RedisTemplate redisTemplate; // Redis模板，用于操作Redis缓存
    @Resource
    private OrderInfoMapper orderInfoMapper; // 订单信息数据访问层
    @Resource
    private OrderStatusLogMapper orderStatusLogMapper; // 订单状态日志数据访问层
    @Resource
    private RedissonClient redissonClient; // 分布式锁客户端
    @Resource
    private OrderMonitorService orderMonitorService;// 订单监控服务
    @Resource
    private OrderBillMapper orderBillMapper;// 订单账单数据访问层
    @Resource
    private OrderProfitsharingMapper orderProfitsharingMapper;// 订单利润分享数据访问层

    @Override
    public Long addOrderInfo(OrderInfoForm orderInfoForm) {
        // 创建订单信息实体对象
        OrderInfo orderInfo = new OrderInfo();
        // 复制属性从表单对象到实体对象
        BeanUtils.copyProperties(orderInfoForm, orderInfo);
        // 生成唯一的订单编号（UUID去除横杠）
        String orderNo = UUID.randomUUID().toString().replaceAll("-", "");
        // 设置订单编号
        orderInfo.setOrderNo(orderNo);
        // 设置订单初始状态为等待接单
        orderInfo.setStatus(OrderStatus.WAITING_ACCEPT.getStatus());
        // 插入订单信息到数据库
        orderInfoMapper.insert(orderInfo);

        // 记录订单状态变更日志
        OrderStatusLog orderStatusLog = new OrderStatusLog();
        // 设置订单ID
        orderStatusLog.setOrderId(orderInfo.getId());
        // 设置订单状态
        orderStatusLog.setOrderStatus(orderInfo.getStatus());
        // 设置操作时间为当前时间
        orderStatusLog.setOperateTime(new Date());
        // 插入状态日志到数据库
        orderStatusLogMapper.insert(orderStatusLog);

        // 在Redis中设置订单接单标记，用于后续业务处理
        redisTemplate.opsForValue().set(RedisConstant.ORDER_ACCEPT_MARK + orderInfo.getId(), "1");

        // 返回新创建的订单ID
        return orderInfo.getId();
    }

    /**
     * 查询订单状态
     *
     * @param orderId 订单ID
     * @return 订单状态
     */
    @Override
    public Integer queryOrderStatus(Long orderId) {
        // 创建Lambda查询条件包装器
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 设置查询条件：选择状态字段，按订单ID进行等值查询
        queryWrapper.select(OrderInfo::getStatus).eq(OrderInfo::getId, orderId);
        // 执行查询，获取订单信息
        OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);
        // 返回订单状态
        return orderInfo.getStatus();
    }

    /**
     * 配送员抢单业务逻辑
     * 实现配送员抢单的并发控制，确保订单只能被一个配送员成功接单
     *
     * @param driverId 配送员ID
     * @param orderId  订单ID
     * @return 抢单结果（当前返回null，建议后续优化为布尔值明确成功/失败）
     */
    @Override
    public Boolean robNewOrder(Long driverId, Long orderId) {
        // 前置检查：判断订单是否处于可抢状态（Redis标记存在则表示可抢）
        // 订单接单标记Key格式：ORDER_ACCEPT_MARK + 订单ID
        if (!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK + orderId)) {
            // 若标记不存在，说明订单已被抢或已失效，抛出抢单失败异常
            throw new PowerException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
        }

        // 获取分布式锁：使用Redisson创建基于订单ID的排他锁，防止并发抢单
        // 锁Key格式：ROB_NEW_ORDER_LOCK + 订单ID（确保同一订单只有一个锁）
        RLock lock = redissonClient.getLock(RedisConstant.ROB_NEW_ORDER_LOCK + orderId);

        try {
            // 尝试获取锁：设置等待时间和自动释放时间（从Redis常量读取配置）
            // 参数1：等待时间（获取锁前最多等待多久）
            // 参数2：租赁时间（获取锁后自动释放时间）
            // 参数3：时间单位（秒）
            boolean flag = lock.tryLock(
                    RedisConstant.ROB_NEW_ORDER_LOCK_WAIT_TIME,
                    RedisConstant.ROB_NEW_ORDER_LOCK_LEASE_TIME,
                    TimeUnit.SECONDS
            );

            // 若成功获取锁，执行抢单逻辑
            if (flag) {
                // 二次检查：再次确认订单可抢状态（防止锁等待期间订单已被抢）
                if (!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK + orderId)) {
                    throw new PowerException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
                }

                // 更新订单状态：创建订单信息对象，设置接单相关属性
                OrderInfo orderInfo = new OrderInfo();
                orderInfo.setId(orderId); // 订单ID
                orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus()); // 状态改为"已接单"
                orderInfo.setAcceptTime(new Date()); // 接单时间（当前时间）
                orderInfo.setDriverId(driverId); // 接单配送员ID

                // 执行数据库更新：根据ID更新订单信息
                orderInfoMapper.updateById(orderInfo);
            }
        } catch (InterruptedException e) {
            // 处理中断异常：将中断异常包装为运行时异常抛出
            throw new RuntimeException(e);
        } finally {
            //释放锁
            if (lock.isLocked())
                lock.unlock();
        }

        // 清理Redis标记：无论抢单成功与否，删除订单可抢标记（防止重复抢单）
        redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK + orderId);

        // 返回结果（当前返回true表示处理完成）
        return true;
    }

    /**
     * 查询顾客当前有效的订单信息
     * 查找顾客处于活跃状态（非已取消/已完成）的最新订单
     *
     * @param customerId 顾客ID
     * @return 订单信息VO，包含订单ID、状态及是否存在当前订单的标识
     */
    @Override
    public CurrentOrderInfoVo searchCustomerCurrentOrder(Long customerId) {
        // 创建Lambda查询条件包装器
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 条件1：订单所属顾客ID等于传入的顾客ID
        queryWrapper.eq(OrderInfo::getCustomerId, customerId);

        // 定义"活跃订单状态"数组：包含从接单到未支付的中间状态（排除已取消/已完成等终态）
        Integer[] statusArray = {
                OrderStatus.ACCEPTED.getStatus(),          // 已接单
                OrderStatus.DRIVER_ARRIVED.getStatus(),    // 配送员已到达
                OrderStatus.UPDATE_CART_INFO.getStatus(),  // 已更新购物车信息
                OrderStatus.START_SERVICE.getStatus(),     // 开始服务
                OrderStatus.END_SERVICE.getStatus(),       // 结束服务
                OrderStatus.UNPAID.getStatus()             // 未支付
        };
        // 条件2：订单状态在活跃状态数组中
        queryWrapper.in(OrderInfo::getStatus, statusArray);
        // 按订单ID降序排序（确保取最新订单）
        queryWrapper.orderByDesc(OrderInfo::getId);
        // 限制只查询1条记录（只返回最新的活跃订单）
        queryWrapper.last("limit 1");

        // 执行查询，获取符合条件的订单信息
        OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);

        // 创建订单信息VO对象（用于前端展示）
        CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo();
        if (orderInfo != null) {
            // 若存在活跃订单：设置订单ID、状态，并标记"存在当前订单"
            currentOrderInfoVo.setOrderId(orderInfo.getId());
            currentOrderInfoVo.setStatus(orderInfo.getStatus());
            currentOrderInfoVo.setIsHasCurrentOrder(true);
        } else {
            // 若不存在活跃订单：仅标记"不存在当前订单"
            currentOrderInfoVo.setIsHasCurrentOrder(false);
        }
        return currentOrderInfoVo;
    }

    /**
     * 查询配送员当前有效的订单信息
     * 查找配送员处于活跃状态（非已取消/已完成）的最新订单
     *
     * @param driverId 配送员ID
     * @return 订单信息VO，包含订单ID、状态及是否存在当前订单的标识
     */
    @Override
    public CurrentOrderInfoVo searchDriverCurrentOrder(Long driverId) {
        // 创建Lambda查询条件包装器
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 条件1：订单所属配送员ID等于传入的配送员ID
        queryWrapper.eq(OrderInfo::getDriverId, driverId);

        // 定义"活跃订单状态"数组：与顾客端共用相同的状态筛选逻辑（从接单到未支付）
        Integer[] statusArray = {
                OrderStatus.ACCEPTED.getStatus(),          // 已接单
                OrderStatus.DRIVER_ARRIVED.getStatus(),    // 配送员已到达
                OrderStatus.UPDATE_CART_INFO.getStatus(),  // 已更新购物车信息
                OrderStatus.START_SERVICE.getStatus(),     // 开始服务
                OrderStatus.END_SERVICE.getStatus(),       // 结束服务
                OrderStatus.UNPAID.getStatus()             // 未支付
        };
        // 条件2：订单状态在活跃状态数组中
        queryWrapper.in(OrderInfo::getStatus, statusArray);
        // 按订单ID降序排序（确保取最新订单）
        queryWrapper.orderByDesc(OrderInfo::getId);
        // 限制只查询1条记录（只返回最新的活跃订单）
        queryWrapper.last("limit 1");

        // 执行查询，获取符合条件的订单信息
        OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);

        // 创建订单信息VO对象（用于前端展示）
        CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo();
        if (orderInfo != null) {
            // 若存在活跃订单：设置订单ID、状态，并标记"存在当前订单"
            currentOrderInfoVo.setOrderId(orderInfo.getId());
            currentOrderInfoVo.setStatus(orderInfo.getStatus());
            currentOrderInfoVo.setIsHasCurrentOrder(true);
        } else {
            // 若不存在活跃订单：仅标记"不存在当前订单"
            currentOrderInfoVo.setIsHasCurrentOrder(false);
        }
        return currentOrderInfoVo;
    }

    /**
     * 司机到达起始位置确认方法
     * 用于更新订单状态为司机已到达，并记录到达时间
     *
     * @param orderId  订单ID
     * @param driverId 司机ID
     * @return Boolean 操作是否成功
     */
    @Override
    public Boolean driverArriveStartLocation(Long orderId, Long driverId) {
        // 创建查询条件构造器，用于精确定位特定订单和司机
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 根据订单ID和司机ID作为查询条件
        queryWrapper.eq(OrderInfo::getId, orderId);
        queryWrapper.eq(OrderInfo::getDriverId, driverId);

        // 创建更新对象，准备更新订单状态
        OrderInfo updateOrderInfo = new OrderInfo();
        // 设置订单状态为司机已到达
        updateOrderInfo.setStatus(OrderStatus.DRIVER_ARRIVED.getStatus());
        // 记录司机到达时间为当前时间
        updateOrderInfo.setArriveTime(new Date());
        // 执行更新操作，根据查询条件更新符合条件的订单
        orderInfoMapper.update(updateOrderInfo, queryWrapper);
        // 返回操作成功结果
        return true;
    }

    /**
     * 更新订单购物车信息方法
     * 用于更新订单的购物车相关信息，并设置订单状态为更新购物车信息状态
     *
     * @param updateOrderCartForm 订单购物车更新表单，包含订单ID、司机ID等要更新的信息
     * @return Boolean 操作是否成功
     */
    @Override
    public Boolean updateOrderCart(UpdateOrderCartForm updateOrderCartForm) {
        // 创建查询条件构造器，用于精确定位特定订单和司机
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 根据订单ID和司机ID作为查询条件
        queryWrapper.eq(OrderInfo::getId, updateOrderCartForm.getOrderId());
        queryWrapper.eq(OrderInfo::getDriverId, updateOrderCartForm.getDriverId());

        // 创建订单更新对象
        OrderInfo updateOrderInfo = new OrderInfo();
        // 使用BeanUtils将表单中的属性值复制到订单对象中
        BeanUtils.copyProperties(updateOrderCartForm, updateOrderInfo);
        // 设置订单状态为更新购物车信息状态
        updateOrderInfo.setStatus(OrderStatus.UPDATE_CART_INFO.getStatus());
        // 执行更新操作，根据查询条件更新符合条件的订单
        orderInfoMapper.update(updateOrderInfo, queryWrapper);
        // 返回操作成功结果
        return true;
    }

    /**
     * 开始服务（司机开始驾驶）
     *
     * @param startDriveForm 开始驾驶表单，包含订单ID和司机ID等信息
     * @return 操作是否成功
     */
    @Override
    public Boolean startDrive(StartDriveForm startDriveForm) {
        // 创建查询条件包装器，用于定位特定的订单
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 条件1：订单ID等于传入的订单ID
        queryWrapper.eq(OrderInfo::getId, startDriveForm.getOrderId());
        // 条件2：司机ID等于传入的司机ID，确保只有订单所属司机才能开始服务
        queryWrapper.eq(OrderInfo::getDriverId, startDriveForm.getDriverId());

        // 创建要更新的订单对象
        OrderInfo updateOrderInfo = new OrderInfo();
        // 复制表单中的属性到订单对象
        BeanUtils.copyProperties(startDriveForm, updateOrderInfo);
        // 设置订单状态为开始服务状态
        updateOrderInfo.setStatus(OrderStatus.START_SERVICE.getStatus());
        // 设置开始服务时间为当前时间
        updateOrderInfo.setStartServiceTime(new Date());

        // 执行更新操作，根据查询条件更新符合条件的订单信息
        orderInfoMapper.update(updateOrderInfo, queryWrapper);
        // 创建订单监控对象
        OrderMonitor orderMonitor = new OrderMonitor();
        // 设置订单ID
        orderMonitor.setOrderId(startDriveForm.getOrderId());
        // 保存订单监控信息
        orderMonitorService.saveOrderMonitor(orderMonitor);
        // 返回操作成功结果
        return true;
    }

    /**
     * 根据时间范围获取订单数量
     * 统计指定时间范围内（[startTime, endTime)）的订单总数
     *
     * @param startTime 开始时间（包含），格式应为可识别的日期时间字符串
     * @param endTime   结束时间（不包含），格式应为可识别的日期时间字符串
     * @return 符合时间条件的订单数量
     */
    @Override
    public Long getOrderNumByTime(String startTime, String endTime) {
        // 创建Lambda查询包装器，用于构建条件查询
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 添加查询条件：服务开始时间大于等于startTime
        queryWrapper.ge(OrderInfo::getStartServiceTime, startTime);
        // 添加查询条件：服务开始时间小于endTime
        queryWrapper.lt(OrderInfo::getStartServiceTime, endTime);
        // 调用Mapper层方法根据条件统计订单数量
        Long count = orderInfoMapper.selectCount(queryWrapper);
        // 返回统计结果
        return count;
    }

    /**
     * 结束服务并结算订单
     * 完成订单的最终结算，包括更新订单状态、创建实际账单和分账信息
     * 所有操作在一个事务中完成，确保数据一致性
     *
     * @param updateOrderBillForm 订单结算表单，包含订单最终的金额、费用等结算信息
     * @return 操作是否成功完成
     */
    @Transactional(rollbackFor = Exception.class)  // 声明事务，遇到任何异常都回滚
    @Override
    public Boolean endDrive(UpdateOrderBillForm updateOrderBillForm) {
        // 创建Lambda查询包装器，用于构建条件查询
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 添加查询条件：订单ID等于传入的订单ID
        queryWrapper.eq(OrderInfo::getId, updateOrderBillForm.getOrderId());
        // 添加查询条件：司机ID等于传入的司机ID（确保只有订单所属司机才能结束服务）
        queryWrapper.eq(OrderInfo::getDriverId, updateOrderBillForm.getDriverId());

        // 创建订单更新对象，设置需要更新的字段
        OrderInfo updateOrderInfo = new OrderInfo();
        // 设置订单状态为服务结束
        updateOrderInfo.setStatus(OrderStatus.END_SERVICE.getStatus());
        // 设置实际支付金额
        updateOrderInfo.setRealAmount(updateOrderBillForm.getTotalAmount());
        // 设置优惠费用
        updateOrderInfo.setFavourFee(updateOrderBillForm.getFavourFee());
        // 设置服务结束时间为当前时间
        updateOrderInfo.setEndServiceTime(new Date());
        // 设置实际行驶距离
        updateOrderInfo.setRealDistance(updateOrderBillForm.getRealDistance());
        // 执行更新操作
        orderInfoMapper.update(updateOrderInfo, queryWrapper);

        // 插入实际账单数据
        OrderBill orderBill = new OrderBill();
        // 复制表单中的属性到账单对象
        BeanUtils.copyProperties(updateOrderBillForm, orderBill);
        // 设置订单ID（确保账单与订单关联）
        orderBill.setOrderId(updateOrderBillForm.getOrderId());
        // 设置支付金额（这里与总金额相同，可能需要根据业务调整）
        orderBill.setPayAmount(orderBill.getTotalAmount());
        // 执行插入操作
        orderBillMapper.insert(orderBill);

        // 插入分账信息数据
        OrderProfitsharing orderProfitsharing = new OrderProfitsharing();
        // 复制表单中的属性到分账对象
        BeanUtils.copyProperties(updateOrderBillForm, orderProfitsharing);
        // 设置订单ID（确保分账信息与订单关联）
        orderProfitsharing.setOrderId(updateOrderBillForm.getOrderId());
        // 设置分账规则ID
        orderProfitsharing.setRuleId(updateOrderBillForm.getProfitsharingRuleId());
        // 设置分账状态为1（表示分账成功或待处理，具体含义需根据业务定义）
        orderProfitsharing.setStatus(1);
        // 执行插入操作
        orderProfitsharingMapper.insert(orderProfitsharing);

        // 返回操作成功结果
        return true;
    }
}
